package utils

import (
	"archive/zip"
	"fmt"
	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/frame/g"
	"io"
	"os"
	"os/exec"
	"path/filepath"
)

func CheckServerIsRunning() bool {
	redisKey := "shapeE-task:is_running"
	varRet, err := g.Redis().DoVar("GET", redisKey)
	if err != nil || varRet.IsNil() {
		return false
	}
	if varRet.String() == "1" {
		return true
	} else {
		return false
	}
}

func GetTaskStatus(taskId string) string {
	redisKey := "shapeE-task:" + taskId
	varRet, err := g.Redis().DoVar("GET", redisKey)
	if err != nil || varRet.IsNil() {
		return "状态未知"
	}
	status := varRet.String()
	if status == "1" {
		return "正在执行"
	}
	if status == "2" {
		return "任务完成"
	}
	return "状态未知"
}

func RunRender(taskGuid string, filePath string, gpuId int) (string, error) {
	status, _ := GetTaskGuidFromRedis(taskGuid)
	if status == CANCEL {
		fmt.Println("任务已取消")
		return "", gerror.New("任务已取消")
	}
	if status == SUCCESS {
		fmt.Println("任务已完成")
		return "", gerror.New("任务已完成")
	}

	fmt.Println("进到RunRender")
	SetTaskGuidToRedis(taskGuid, PROCESSING)

	pythonPath := g.Cfg().GetString("python_path")
	fmt.Println("pythonPath:", pythonPath)
	script := g.Cfg().GetString("script_file")
	fmt.Println("script:", script)
	//args := []string{script, taskGuid, prompt, fmt.Sprintf("%d", gpuId)}
	//fmt.Println("args:", args)

	outputPath := g.Cfg().GetString("dir_path")
	outPutDir := filepath.Join(outputPath, taskGuid)
	huggingfaceModel := g.Cfg().GetString("huggingface_model_path")

	args := []string{script, filePath, "--output-dir", outPutDir, "--device", "cuda:" + fmt.Sprintf("%d", gpuId), "--render", "--pretrained-model-name-or-path", huggingfaceModel}

	output, err := exec.Command(pythonPath, args...).Output()
	if err != nil {
		fmt.Println("output===", string(output))
		SetTaskGuidToRedis(taskGuid, FAILED)
		fmt.Println(err)
		return taskGuid, err
	}
	fmt.Println(string(output))

	srcPath := g.Cfg().GetString("dir_path") + "/" + taskGuid

	dstZipFile := g.Cfg().GetString("dir_path") + "/" + taskGuid + "/" + "0" + "/" + taskGuid + ".zip"
	//srcPath := g.Cfg().GetString("dir_path") + taskGuid

	MakeZipFile(dstZipFile, srcPath)

	SetTaskGuidToRedis(taskGuid, SUCCESS)

	return taskGuid, nil

}

// 定义一个结构体，用于存储目标文件
// 专门定义结构体是为了方便存储压缩包中的目录结构
type TargetFile struct {
	Path         string // 目标文件路径
	RelativePath string // 压缩文件的相对路径
}

// 获取指定路径目标压缩文件列表
// path : 压缩目标文件的路径
// relativePath : 在压缩包中的相对路径
func ListTargetFiles(path string, relativePath string) (targetFiles []*TargetFile, err error) {
	targetFiles = make([]*TargetFile, 0)
	finfo, err := os.Stat(path)
	if err != nil {
		return
	}
	// 如果不是目录，则直接将文件加入文件列表
	if !finfo.IsDir() {
		targetFile := &TargetFile{
			Path:         path,
			RelativePath: relativePath + finfo.Name(),
		}
		targetFiles = append(targetFiles, targetFile)
		return
	}
	// 遍历目录
	dstFile, err := os.Open(path)
	if err != nil {
		return
	}
	fileInfos, err := dstFile.Readdir(-1)
	if err != nil {
		return
	}
	for _, fi := range fileInfos {
		if fi.IsDir() {
			files, err := ListTargetFiles(path+"/"+fi.Name(), relativePath+fi.Name()+"/")
			if err != nil {
				return nil, err
			}
			targetFiles = append(targetFiles, files...)
			continue
		}
		targetFile := &TargetFile{
			Path:         path + "/" + fi.Name(),
			RelativePath: relativePath + fi.Name(),
		}
		targetFiles = append(targetFiles, targetFile)
	}
	return
}

// 制作一个zip文件（打包zip）
// dstZipFile : 输出的目标zip文件
// srcPath : 压缩的目标路径，可以是文件也可以是目录
func MakeZipFile(dstZipFile, srcPath string) error {
	targetFiles, err := ListTargetFiles(srcPath, "")
	if err != nil {
		return err
	}
	// 将文件列表添加到压缩文件
	//zipFile, err := os.Create("archive.zip")
	zipFile, err := os.Create(dstZipFile)
	if err != nil {
		return err
	}
	// 创建一个Writer
	zipWriter := zip.NewWriter(zipFile)
	for _, f := range targetFiles {
		objFile, err := os.Open(f.Path)
		if err != nil {
			return err
		}
		// 关闭文件，注意，因为这里的objFile在每个循环中的值不一样
		// 所以这里应该以闭包的形式实现，不应该直接使用defer objFile.Close()
		w, err := zipWriter.Create(f.RelativePath)
		if err != nil {
			return err
		}
		_, err = io.Copy(w, objFile)
		if err != nil {
			return err
		}
		objFile.Close()
	}
	zipWriter.Close()
	//zipFile.Close()
	return nil
}

func ListFileALLPath(dirPth string) (files []string, err error) {
	files = make([]string, 0, 30)
	dir, err := os.Open(dirPth)
	if err != nil {
		return files, err
	}
	defer dir.Close()
	names, err := dir.Readdirnames(0)
	if err != nil {
		return files, err
	}
	for _, name := range names {
		files = append(files, dirPth+"/"+name)
	}
	return files, nil
}
